home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-02
/
pnl005.zip
/
PNL005.TXT
< prev
next >
Wrap
Text File
|
1990-12-20
|
102KB
|
2,129 lines
//////// // // //
// // /// // //
// // //// // //
//////// // // // //
// // //// //
// // /// //
// // // ///////
Pascal NewsLetter
Issue #5
December, 1990
Editor: Pete Davis
The Programmer's Forum BBS is the home of
PNL. It can be reached in Washington, DC at
(202)966-3647. Information is available
through the following locations:
FidoNet: Pete Davis@1:109/138
GEnie: P.DAVIS5
BitNet: HJ647C@GWUVM & UE356C@GWUVM
InterNet: HJ647C@GWUVM.GWU.EDU
or UE356C@GWUVM.GWU.EDU
Uucp: Pete.Davis@f138.n109.z1.fidonet.org
2
Table of Contents
Introduction ............................... 3 (P.D.)
The Lazy Programmer's Guide
to Graphics in Pascal ...................... 4 (R.M.)
Introduction to Multiple Concept
Pascal Programming ......................... 15 (P.R.)
For Beginners .............................. 22 (B.G.)
Recursion is Easy .......................... 32 (C.B.)
Conclusion ................................. 38 (P.D.)
Distribution List .......................... 40
P.D. -> Pete Davis (Editor-in-Chief, Writer)
R.M. -> Richard A. Morris (Editor-Over-the-Pond, Writer)
B.G. -> Bob Gowans (Staff writer)
P.R. -> Paul Robinson (Contributing writer)
C.B. -> Chris Burke (Contributing writer)
3
Introduction
Well, finally getting to another issue of the Pascal
NewsLetter. We're making a few changes in the organization
of PNL. First of all, I've promoted myself to Editor-in-
Chief. That's really what I've been all along, but it was to
make room for some new titles.
We've got a new staffer, Richard A. Morris, with the
distinguished title of 'Editor-Over-the-Pond'. Richard will
be a collection point in Australia for PNL contributions and
will be editing and sending them on to me for inclusion in
the NewsLetter. For a profile of Richard, read the end of
his article. If you live in Australia, send your
Contributions to Richard A. Morris. His Fido-Net Address is
3:640/378. His system's phone number is 61-7-878-1194.
Richard is also writing "The Lazy Programmer's Guide to
Graphics in Pascal", a series of articles about, you guessed
it, graphics programming in Pascal. It's very thorough
coverage of graphics, so many of you will surely find it
interesting.
Many of you probably already know that I had been
planning on putting the newsletter out as 'Subscription-
only'. Well, I ran into some problems un-related to the
newsletter which have forced me to postpone, at least until
summer, this course.
Also, I would like to thank everyone who has been
contributing. It seems that the number of submissions is
increasing slightly, which is a terrific bonus. If you can
keep them coming in, the newsletter just might find a way to
come out a bit more often.
In this issue, Paul Robinson will be talking a bit
about writing large programs in Pascal and will cover some
programming techniques. Bob Gowans continues his popular
"For Beginner's" column, and last, but not least, Chris
Burke shows you how simple recursion can be.
Well, that's about it for this issue. I hope you enjoy
it.
4
The Lazy Programmers Guide to Graphics in Pascal
by Richard A. Morris
Fido 3:640/378
Brisbane, AUSTRALIA
I have been running a BBS and reading FIDO echoes for
some years now and invariably the first 2 things Novice
programmers want to write with their new language are a
better BBS, and a better Graphic Game. Well I'll leave the
intricate subject of Data communications to the "Build a
better Mousetrap "experts, and Game theory to the Academics,
Rather in the next 6 Articles I'll discuss Graphics
programming with Turbo Pascal.
The Idea behind the Lazy programmers Guide, is that if
you are like me, you often wondered how easy it would be to
do some particular programming task, but never had the time
or inclination to get around to opening the manual at that
section. Well this article is both for the novice, and the
experienced (Lazy Programmer) and will take you from very
simplistic exercises to quite complex Graphic programming in
a short time, by program examples, and prodding your
imagination to experiment a bit :-).
These articles are based on Turbo Pascal on the PC, and
I apologize in advance to those not using that, in
particular versions 4, 5, and 5.5 (And I apologize in
Advance to all Americans as I intend to use the word Color a
lot :-). I am sure that the topics I intend to cover will
have some relevance for all Graphic programming. I don't
promise that you will be able to write a better Flight
Simulator (Although if you do I want a mention :-) but you
will understand a bit more about how it's done, and probably
gain a lot of respect for these Graphic Wizards.
The articles will assume that you have some knowledge
of programming concepts (Such as Binary mathematics,
Trigonometry) and Turbo Pascal (there will be very little
handholding :-), and will take you from simple graphics to
quite Advanced programming concepts.
The Articles will comprise the following Topics;
Basic Graphics with Turbo Pascal GRAPH unit.
Graphs, Statistics, and windows with the GRAPH unit.
Simple Animation and Sprites using the GRAPH unit.
User defined programming in the GRAPH Unit
Writing Directly to the EGA/VGA.
Animation using Direct writes to the EGA/VGA.
Three Dimensional Graphics directly to the EGA/VGA.
5
------------------------------------------------------------
The Lazy Programmers guide to Turbo Pascal Graphics - Part 1
- Basic Graphics with the GRAPH unit -
------------------------------------------------------------
A Brief History of PC Displays - Apologies to S. Hawkins
:-).
------------------------------------------------------------
Graphics in the Personal computer world refer to
non-text display information. The original PC released a
decade ago, was equipped with a display called the
Monochrome Display, and a driver called (Surprise surprise)
the Monochrome Display Adapter. This Display had a
resolution of 720x350 and displayed ONLY the IBM PC
character set in black and white.
It was not long before the popularity of computers such
as the Apple II, with it's bit mapped display, caused demand
for a graphic display, and the Hercules (from a PC add-on
company Hercules Computer Technology) was born. The
Hercules still displayed data in black and white, but it
allowed you to show bitmapped graphics. Soon IBM got the
hint and released the Color Graphics Adapter with 16 color
text and 4 color bit mapped graphics, unfortunately the CGA
design featured a lot of compromises such as display to
inferior composite displays, and offered poor display
resolution (320x200), but it did have Color, and it is one
of the most popular displays around.
Eventually due to increasing market pressure from
companies like Apple, Atari, and Commodore, IBM brought out
a reasonable display called the ECD, and drove it with the
Enhanced Color Adapter, this gave programmers access to a
resolution of 640x350 with 16 simultaneous Colors from a
palette of 64. Lately VGA technology has become a standard
and it increases displays up to 640x480 and 256 colors (at
320x200). These articles will concentrate on the EGA and
VGA modes, but the GRAPH unit stuff will cover some aspects
of general graphic device programming, and this first part
only requires a CGA Screen..
BGI - The Borland Graphics Interface
------------------------------------
Due to the nature of the PC market, any programmer who
wrote his code for any particular Graphic standard limited
his earnings by limiting his User base to only that machine.
True, most of the standards are Downward compatible, which
6
means that a CGA only application would run on a VGA, but
given the choice a VGA owner would surely choose a product
that displayed VGA graphics.
Until Borland brought out BGI's most Languages did not
support Graphics at all, relying on third party toolboxes,
with Turbo Pascal and Turbo C Borland introduced the concept
of a Graphic Interface whereby a programmer wrote code to
call the BGI which would then do all the hard work of
working out what device it was talking to and how to display
that information.
This is a great concept, as it provides portability of
programs across graphic devices, but of late a lot of
programmers have decided to bypass Borlands interface to
talk instead directly with the graphic device. The main
reason for this is speed, Borland Drivers use the computer
BIOS in a lot of cases (Which itself wastes time deciding
what device it is talking to), and are themselves quite
slow, hence I shall devote a fair bit of time in later
Articles on programming Directly to the Graphic device.
Setting up to display Graphics
------------------------------
Firstly to Show graphics on our monitor we must tell the
device to get into graphics mode, at the end of displaying
what we want we must also return the system to the original
display mode. Additionally as we will be using BGIs during
this article, we must tell Turbo pascal where to find our
BGI control files.
A sample of a simple program to get into graphics mode
display a line of text (More of this in this article), and
go back to text mode is in the following program put it into
a file called GRAPH_TE.PAS. To compile it load it into the
editor, change line 7 to make BGI_Directory point to the
directory you have put all the BGI files into (eg:I use
c:\TP\BGI).
~ Prog 1 ~~~~~~GRAPH_TE.PAS~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{$M 16384,16384,655360}
Program Graph_Test;
Uses
CRT,
GRAPH;
Const
BGI_Directory = 'C:\TP\BGI';
var
GraphDriver : Integer;
GraphMode : Integer;
7
ErrorCode : Integer;
tempstr : String;
begin
GraphDriver := Detect;
InitGraph(GraphDriver,GraphMode,BGI_Directory);
ErrorCode := GraphResult;
if ErrorCode <> grok then
begin
Writeln('Error initialising Graphics :',ErrorCode);
Writeln('Error Message :',GraphErrorMsg(ErrorCode));
Halt(1);
end;
{Graphics stuff begins}
Str(GraphMode,TempStr);
OutText('We are in Graphics mode number :'+TempStr);
Str(GraphDriver,TempStr);
OutText('This is Device number :'+TempStr);
{End of Graphics stuff}
delay(1000);
CloseGraph;
end.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
O.K. let's dissect this test program, firstly in line 4
we USE the GRAPH unit, this will give us access to all of
the graphics routines, and constants. We declare the
directory that all the BGI files will be found into a
constant, and declare 4 variables GraphDriver to store the
type of graphics device we have, GraphMode to store the
graphics mode we will use, ErrorCode to store any error
numbers, and TempStr to temporarily store integers as
strings.
Then we set GraphDriver to the GRAPH constant DETECT.
DETECT is defined as 0 and will tell Turbo Pascal to
automatically detect the Graphic device connected to the PC,
and select for us the maximum available Graphics mode. The
next line calls the GRAPH function INITGRAPH, we tell this
function what device to talk to (or AUTO detect in our
case), which mode we want (In this case the Auto detect will
select one for us), and the location of the BGI files. We
can actually pre-select the type of device by specifying the
GraphDriver and GraphMode before running InitGraph, as in
the following inset (Replace Grafdriver := detect).
~~~~~~~~~~~~~~~~~~~~~~
GraphDriver := CGA;
GraphMode := cgac0;
~~~~~~~~~~~~~~~~~~~~~~
8
This will set up the Graphic device for working with a
CGA compatible device, running in mode CGAc0, or the first
CGA color mode. InitGraph allocates some memory on the heap
to work with, so you must have some heap memory available
(Hence the $M directive in line 1), such as a general 4K
buffer for graphics, and space to put the BGI file.
The GRAPH function GraphResult will have an errorcode
after most GRAPH routines and will be zero if the routine
proceeded without error, or will contain an error number if
an error occurred, the function GraphErrorMsg can give you a
textual error message if you send it the error number as we
have in the test program. If an error occurs the program
describes the error and aborts.
Assuming successful initialization of the Graphic
system the next step is to perform the Graphics routines,
which in this case is to write out a message describing the
Device that autodetect found and the Mode. We have to use
the str routine to convert integers into strings, as the
Graph text writing expects a string as a parameter, and
can't use the abilities of write and writeln. Finally we
close the graphics drivers with CLOSEGRAPH. Close Graph
releases all memory assigned by InitGraph and restores the
original text mode.
Now when you run the program it will quickly display
the graphics data, and jump back to text mode, so I've
inserted a delay in before the call to close graph. You may
notice a delay before loading graphics while the system
loads the BGI file, in some systems this may be unacceptable
or you may wish to distribute just on EXE file and not
include a whole lot of BGIs, so Borland have given you a
demo of how to do this in the example file BGILINK.PAS.
If you have problems with this sample file you should
check that you have a graphics capable device, and that the
BGI files are indeed where you specified them.
Displaying Pixels
-----------------
O.K. so far you've managed to Display some funny shaped
text on the screen, Big Deal you say. Well from here on in
this part we will use the program Graph_Test, to set up the
graphics device, and close it after use, and in place of the
above Graphics stuff (In this case the OUTTEXTs) we put in
specialized code. The next step is to display a point on
the screen, we call one of these a PiXel or Picture Element.
As you know different Display technologies have
9
different Screen resolutions (Pixels on the Screen) for
example you fit 320 pixels across and 200 down in CGA mode
5. In the BGI routines, you can put a pixel in a location
that is not on the screen. This may seem strange, but when
you realize that under EGA you can put a pixel in the last
column at column 639 (the first column is always 0 so 0-639
= 640 pixels), which would be way of the screen in CGA. For
the purposes of this Article we'll do most of our work in
the range of 0 - 99 columns and 1 - 99 rows, which will be
displayable on all monitors.
The routine to display a pixel on the screen is
(Surprise) PutPixel(x,y : Integer; Color: word) X and Y are
not surprisingly Column and Row coordinates, and Color is
the color of the pixel. O.K. so lets do some Graphics.
Insert the following declarations at the beginning of
Graph_Test, and the routine in the Graphics stuff section.
~ Declarations ~~~~~~~~~~~~~~~~~~~~~~~~
VAR
Counter : Integer;
Color : Word;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Color := 1;
For Counter := 0 to 99 do
begin
PutPixel(0 ,Counter,color);
PutPixel(99 ,Counter,color);
PutPixel(Counter,0 ,color);
PutPixel(Counter,99 ,color);
end;
PutPixel(50 ,50 ,Color);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This routine will Firstly set the variable color to 1
(The first available color, in which case all devices will
show a color distinct from the background color), which in
most modes is Dark blue, in the 320x200 cga modes this will
be Green, or Cyan. The system then counts from 0 to 99 and
puts a box on the screen, by plotting a point in the left,
right, top and Bottom edge. To see how it does this dot by
dot put the line DELAY(100) before the end in the FOR loop,
you will see each dot being drawn. Then it will put a dot
almost in the middle of the box, depending on the resolution
of your display mode, the box could be anything from a
perfect square to an elongated rectangle, GRAPH has a
procedure called GetAspectRatio which will tell you the
ratio you may use to draw a perfect square (We'll talk about
this later on), and various functions and procedures to ask
the Graphics device how wide and tall (in pixels) it is, we
will discuss these in the next article on Graphs.
10
Play with the colors for your default (Auto
detected) mode, then try playing with the Graphdevice and
Graph mode, to get the hang of your Graphics device, as you
will need to know later on about the capabilities of
your system. You can also move the x,y position of the
single pixel, or even draw some simple shapes.
An example of a little function to draw a shape would
be to stick this line in the FOR loop.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PutPixel(Counter,50+trunc(sin((counter/100)*2*pi)*20),color)
;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It plots a point for each x position of counter, based
on a sin function (A Turbo function that gives a value on a
sin curve (Wave !?!) between -1 and 1 for every position
between 0 and 2*Pi radians). TRUNC converts the Sin value
from a Real into an integer for PutPixels Y value,
sin((Counter/100)*2*pi) gives a real between -1 and 1 as
(Counter/100) goes from 0 to almost 1, and we multiply this
by 20 to make the wave oscillate between 50-20 and 50+20 to
put the wave in the middle of the box.
Lines, Arcs and other Objects
-----------------------------
Drawing a line is a bit slow plotting a pixel at a
time, so Turbos GRAPH unit includes a LINE function so
firstly we'll replace our FOR Loop box Drawing routines
with;
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetLineStyle(SolidLn,0,NormWidth);
SetColor(1);
Line(0,0,0,99);
Line(0,99,99,99);
Line(99,99,99,0);
Line(99,0,0,0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You will also notice that LINE doesn't include a color
parameter, GRAPH has a routine called SETCOLOR (Which we set
before we draw the lines) which defines a color to be used
by all line and object drawing routines. Graph has another
surprise with a routine called SETLINESTYLE which allows us
to select the style, pattern, and thickness of the lines
11
we'll draw, again I've included that one above for a
SolidLn, but you might like to try styles of DottedLn,
CentreLn, or DashedLn and a Thickness of Thickwidth.
Pattern is a special value we'll discuss in the next topic.
There is an even Better method to display a Box
called RECTANGLE, which combined the 4 lines commands, try
this.
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetLineStyle(SolidLn,0,NormWidth);
SetColor(1);
Rectangle(0,0,99,99);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now try this, it uses three more line based object
drawing routines;
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetLineStyle(SolidLn,0,NormWidth);
SetColor(1);
Rectangle(0,0,99,99);
Circle(50,50,40);
Arc(50,60,225,315,20);
Ellipse(40,40,0,360,5,10);
Ellipse(60,40,0,360,5,10);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
What these routines do is Draw a Circle with the centre
at 50,50 with a radius of 40 pixels (This routine uses
GetAspectRatio to makes as round a circle as possible), Arc
draws a part of a circle with a centre at 50,60 (10 pixels
below the centre of the circle) going from 225 degrees
(Unlike the Trigonometric functions the Graphics routines
represent angles in Degrees, starting from 0 horizontally to
the right) to 315 degrees with a radius of 20 pixels, the
two Ellipses draw two identical squashed circles with
centres 10 pixels either side of the middle point, and 10
pixels above it, each ellipse goes from angle 0 around a
full circle to 360, and with a x radius of 5 and a Y radius
of 10. If this explanation seems a bit obtuse, run the demo
first and you'll see what I mean <grin>.
The Last Line Based Object drawing routine is the
PolyDraw routine which will draw any polygon, although it
needs a bit of setting up. What you have to do is define a
variable as an array of pointType which is a GRAPH global
type, a record of (x,y) coordinates. Then you fill the
array elements with x,y coordinate data (I often use Graph
paper to work out how I'm going to do a shape), and simply
call the DrawPoly routine with the number of points, and the
Shapearray, try this one which draws two triangles on our
face shape;
~ Delcaration ~~~~~~~~~~~~~~~~~~~~~~~~~
12
Triangle : Array[1..4] of PointType;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Triangle[1].x := 50;
Triangle[1].y := 85;
Triangle[2].x := 20;
Triangle[2].y := 75;
Triangle[3].x := 20;
Triangle[3].y := 95;
Triangle[4].x := 50;
Triangle[4].y := 85;
DrawPoly(4, Triangle);
Triangle[2].x := 80;
Triangle[3].x := 80;
DrawPoly(4, Triangle);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Patterns and fills
------------------
O.K. from the previous program, add the following lines
after that last DrawPoly routine;
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetFillStyle(solidFill,Color);
FloodFill(50,5,Color);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A fill is a bit like a color-in that kids do, it will
fill an area with a color. Now this one takes a bit of
explaining, firstly SetFillStyle sets the type of fill any
solid routine will use, and the color that it will color in
with, in this case it will be a SolidFill, you can fill with
diagonal lines (SlashFill), or hatchs (HatchFill), dots
(CloseDotFill) or others, play around with it. The second
line calls the fill, by specifying the x and y position of
any point within the area to fill, and the color that will
border the area.
If there are any breaks in the border then the flood
fill will leak through, try putting this routine to remove
one of the bowties edges in before the FloodFill Routine,
you'll notice the floodfill will bleed into the second
triangle;
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetColor(0);
Line(Triangle[2].x,Triangle[2].y,Triangle[3].x,Triangle[3].y
);
SetColor(1);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can draw the polygon and fill at the same time,
13
with a GRAPH routine called FillPoly, remove the previous
routine to delete the edge of the bowtie, remove the
floodfill, and Change the two DRAWPOLYs into FILLPOLLY and
put
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetFillStyle(WideDotFill,Color);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
anywhere before both FillPolys and the routines will perform
a poly draw AND a fill at the same time. You can use
FillEllipse in the same way to draw and fill an ellipse, or
Bar to draw and fill a rectangle.
Text and Fonts
--------------
To draw text there are several commands the first
SetTextStyle loads up BGI files for the Font type (Much like
initgraph sets up the Device BGI files), sets the direction
of the text (Try this next procedure with VertDir instead of
HorizDir, and set the text to start at 10,5), and the size
of the characters. The second Command OutTextXY(X,Y :
Integer;Text : String) sends text of the defined Style to
the output port at the requested X,Y location.
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetTextStyle(DefaultFont,HorizDir,1);
OutTextXY(20,5,'Mr Happy');
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Try different commands such as different fonts
(TriplexFont, GothicFont), different sizes (Each fonts text
will be a different size), and different orientations.
One major problem with Ad hoc text placement in
Graphics modes is working out where to put the next line, as
you probably won't know where the current text finishes and
the next should begin. Turbo provides two procedures
TextHeight and TextWidth for just this problem. Simply said
they return the size in pixels take up by a string. Let's
go straight to an example, and I'll show you what I mean.
Remove all the old graphic stuff leaving only the Box
routine, and after drawing the box, add the following;
~ Routine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetTextStyle(DefaultFont,HorizDir,1);
Charheight := TestHeight('M');
OutTextXY(20,5,'Simple example');
OutTextXY(20,5+textheight,'of text spaced');
OutTextXY(20,5+textheight+textheight,'at even heights');
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14
As you see you will get each new line starting after
the previous line has finished, regardless of font,
character, or graphics device.
Summary
-------
Well I hope I haven't offended too many sensibilities
with the simplistic nature of the exercises, we have gone
over some very basic Graphic concepts here, that are
fundamental to further understanding graphic manipulation.
Next PNL I will be discussing Business, mathematic and
Scientific Graphics such as Bar, Pie, Scatter, Hi_Lo graphs,
the graphing of mathematic functions in 2 and 3 dimensions,
and Fractal representations such as Sierpenski, Mandlebrot,
and Benoit sets. As well as topics, like windows, Auto
Scaling graph axes, and further discussion of aspects of
text drawing.
Author
------
Richard Morris is the Owner/operator of KHIRON
Software, in Brisbane AUSTRALIA, a company that specializes
in Graphic Databases in Turbo Pascal for Health care
professionals, as well as providing contract staff for as
diverse projects as Robotic process control software, to
various Industry support software. Current Supported
Packages DENTAPAC tm - Dental practice management tools,
PHYSIOPAC tm - Phyiotherapy management tools, and ORTHOPAC -
Orthodontic practice management tools.
References
-----------
The Turbo Pascal Reference Guide
(C)89 Borland Int.
Advanced Programmer's Guide to the EGA/VGA
George Sutty/Steve Blair
Advanced MsDos
Ray Duncan
INTER490
Ralf Brown
15
(C) Copyright 1990, Tansin A. Darcos & Co. Commercial
Reproduction rights are reserved.
Introduction to Multiple-Concept Pascal Programs
Paul Robinson
Most large or commercial programs tend to do a number
of things. I was trying to do a program that would count
all the words in a text file - which might be almost any
line format, and produce an alphabetical list of words.
This meant that I had to do several things:
o Read a file that might have Carriage Return
("CR") followed by Line Feed ("LF") for an
end of line, LF then CR, LF only, or CR
only, or might be from one of those weird
programs that just generates "stream ASCII"
in which it takes words until the line is
full then at the next space issues a "soft"
carriage return. The only requirement was
that the file be non-coded, i.e. that
characters appear as normal character values
(Wordstar, for example, puts a high-bit code
on some words to use as paragraph or line
breaks).
o When reading the words, store them in memory. The
program might read a small file or it might
read a 1/2 million word book, so the number
of words would be indeterminant, and the
word sizes would all be different.
o List each word and the number of times used, in
alphabetical order. The first qualification
precluded the use of Pascal's standard
READLN/EOLN construct to check for end of
line. This meant I'd have to read the file
in on a character by character basis. The
second provision was because, with as much
memory as is on a PC, there is really no need
- unless you get a huge file - to use disk
space to store word counts (also because of
something I was trying to do that I wanted to
use a in-memory word list). The third
provision was what I had wanted: an
alphabetical word list. (If I had been more
concerned with the number of uses, I would
have had the words listed in order by number
of usages rather than alphabetically). These
provisions made the choice of using a
linked list quite obvious. The questions that
came up included what type of linked list to
16
use and how to implement it. That will be
discussed below.
Understanding Pointer Variables
Paul Robinson
One of the things I needed to do one day was read one
or more disk files and create an alphabetized list of the
words in each file, for something I was working on. I
wanted to store the list of words used in the file in
memory because I did not want to spend a lot of time reading
and writing the disk in addition to all the disk reads
being used to read the original file(s). Since the number
of words to be stored would vary, storing them in
arrays might either not provide enough memory space or waste
a lot of memory.
Pascal (and other languages) have a capability in
which a variable can point to a block of storage that
only exists after the program explicitly requests to be
issued that storage. Arrays and variables are created (for
the main program) when it is executed, and exist until the
program ends. This special storage is allocated
dynamically if, as and when needed, by the programmer
specifically asking to be given a block of this
memory. The programmer can also give back this memory
when finished using it, and perhaps even re-use it later on.
To access this dynamic memory, the programmer creates a
class of variable called a pointer. A pointer is a
variable that describes a memory location indirectly. Let
me explain that a little closer.
An ordinary variable exists at a specific location in
memory, and contains a value such as a number, or a
character (or two). This is an ordinary, direct access to a
value. A pointer, however, goes one step further, in that
it contains not a value, but the address of a value.
For example, let me examine a record:
TYPE
maxstring = STRING[255];
tokp = ^tok ;
tok = RECORD
next : tokp ;
count : LONGINT ;
token : maxstring;
END ;
17
The first item indicates that the identifier
'maxstring' is a string of the maximum size. Next, I
indicate that the identifier 'tokp' is a pointer to (that's
what the ^ indicates: "Pointer to") the identifier 'tok'.
But tok is not defined until after tokp. This is the one
exception to the rule in Pascal that everything must be
defined before use; pointers may refer to identifiers which
are not defined when the pointer is.
Tok is a record beginning with a 'next' field, which
since we know tokp is a pointer, that 'next' is a pointer
to another record of the same type. The 'count' variable is
a long integer, and 'token' is a string of maximum length.
Why is there yet another pointer when one is already
defined? The 'next' field is what is called a 'traverse'
pointer. In order to find records in a linked list, you
have to be able to follow ('traverse') the list in whatever
order it is maintained. The way to do this is to change
the pointer that looks at a record from the current
record to the next one in the list. This would be done
by assigning the 'next' value to the pointer which
is referencing this record. Some pointer records will
also carry a 'back' or 'prev' pointer to go backward in the
list. Note that all this has done is create a set of
names so that we can use this record - or the tokp pointer-
in the program, but we cannot yet use them because we did
not define variables that do, like this:
VAR
name:maxstring;
T,t1: tokp;
We still can't access the memory, because the
pointers do not really contain the data, they contain only
the address of the data. We have to allocate the space
for that data in order to be able to access them. There
are two ways that a programmer can be given access to
dynamic memory. Both require the programmer pass a
pointer to a procedure that allocates memory to that
pointer.
The first method is the NEW procedure, which is called
in the form
NEW(T);
however, in our instance, allocating 255 bytes for each
word probably wastes memory, especially since each word
probably is no larger than 20 characters. There are two ways
around this: pick a maximum and assume no word will be
larger than that, or size the records to fit, which is
option 2.
18
The second method involves knowing what the program
does; since I will know that 'name' will contain the string
to be stored in the 'token' field of the tok record. So,
I could allocate a specific sized block of memory with a
special procedure available in Turbo Pascal called GETMEM,
and it works like this:
GETMEM(T,LENGTH(NAME)+10);
where the first argument is the pointer to use, and the
second argument is the amount of memory in bytes to allocate
to the pointer. The LENGTH(NAME) value gives us the actual
size of the string, the +10 is because the pointer takes 4
bytes, the longint takes 4 bytes, the length of the string
adds one byte, and I want the next allocation to come on a
word boundary.
In either the NEW or GETMEM options, the variable is
now ready for use. We can access it by referencing the
pointer followed by a fieldname:
T^.Count := 1; or T^.Token := Name;
T^.Token := Name; T^.Count := 1;
Also, it is usually best to ensure that any unused
or traverse pointers are cleared, so we should also say:
T^.Next := NIL;
(NIL is a special value which indicates a pointer is not in
use).
If we later add another record, we have to keep the
old one, so we might do something like this:
T1 := T;
WHILE T1^.Next <> NIL DO
T1 := T1^.NEXT;
GETMEM(T1^.Next,LENGTH(Name)+10);
T1 := T1^.Next;
(I am trying to simplify this example. Things like
alphabetizing the entries, checking for duplicates, etc.,
has been omitted.)
When finished with the memory pointed to by a pointer,
you release it back to the system. On some systems they use
MARK and RELEASE, in which you create a mark pointer, and
store the current memory list in the marked pointer with
MARK(markpointer);
19
then when you don't need the memory any more, you get rid of
it with
RELEASE(markpointer);
This cancels all usage of the NEW procedure done
after the MARK procedure call. Any pointers allocated after
that MARK are now invalid. Some systems do a better job
at allocating memory, in which you use the NEW procedure (or
on Turbo Pascal, you can also use GETMEM), then when
you don't need a particular piece of memory, you can use
DISPOSE (if you called NEW) or FREEMEM (if you called
GETMEM). You would use them like this:
NEW(T1);
...
DISPOSE(T1);
(* OR *)
GETMEM(T1,LENGTH(NAME)+10);
...
FREEMEM(T1,LENGTH(NAME)+10);
Note: if a program is exiting, it does not have to
release the memory it allocated; the system will do so for
it.
Pointers do not have to point to records. In some
systems, pointers are used as a means to simulate the
PEEK/POKE function from BASIC, as in the following
example:
PP = ^CHAR;
VAR
P: PP;
I: array[1..2] of integer absolute P;
Colr,Mono,Both:boolean;
BEGIN
I[2] := $B800;
I[1] := 0;
Colr := FALSE;
Mono := FALSE;
P^ := 'A';
IF P^ = 'A' THEN Colr := TRUE;
I[2] := $B000;
20
I[1] := 0;
P^ := 'A';
IF P^ = 'A' THEN Mono := TRUE;
IF Colr and Mono THEN Both := True;
...
This little program segment checks to see if a color
adapter is installed, then it checks to see if a monochrome
adapter is installed, by checking the physical addresses
to see if there is memory in their spaces. This piece of
code is here to show that a pointer can point to
something other than a record. Turbo Pascal has the
MemW[] and Mem[] arrays, but some compilers do not, and this
is one way it may be simulated.
Understanding A Linked List
Paul Robinson
Going back to my original idea, I wanted to create
an alphabetized word list. I wanted merely to do a
single linked list where each item points to the next one in
memory. If I checked the words first, I could store the
table automatically in alphabetical order and thus
generating the table would be trivial.
Almost all the books and articles I see deal only
with double-linked lists, where there is a pointer to a
previous record, and a pointer to a record that is next in
line. This would be overcomplicated for something as
simple as just generating a list of words in
alphabetical order. I didn't want frequency counts in a
binary tree, I just wanted a list of words - and it seemed
trivial.
I have used Pascal for over 5 years and doing this
little exercise taught me something about the language and
how it works. It also taught me a little about the way I
was thinking about certain concepts.
Every time I started on the means to store the table, I
kept either losing items or not storing the new entries.
The standard method for working with a linked list is to
have a start pointer, and additionally to have a 'traverse'
pointer, as described above. You follow down the links
until you find the entry you want or until you run out of
items.
One thing I had misunderstood was that when you have a
second pointer, the pointer is not the data, and two
pointers pointing to the same place are not the same
21
thing. This means, if you have
var
T,T1:Tokp;
name:maxstring;
begin
NEW(T);
T1 := T;
That any change to T1 will not have any effect on the
system, unless you do something to one of the fields pointed
to by t1. Remember, just because T and T1 point to the
same place do not make them the same thing. And that is
what caused me so much trouble.
In the enclosed program (WORDCOUNT.PAS) is the word
count program I wrote. If you notice the section where T2 is
allocated then the values are all moved:
IF t1^.token > tab.sym THEN
BEGIN
new(t2);
tab.nwords := tab.nwords+1;
t2^.count := t1^.count;
t2^.token := t1^.token;
t2^.next := t1^.next;
t1^.count := 1;
t1^.token := tab.sym;
t1^.next := t2;
END;
Merely assigning values to T2 would not have worked,
because T2 is local to that procedure. The only way to
get the values to stick is to use the existing values and
attach these.
This program sample was added to fix two glaring
deficiencies: the lack of an explanation in the use of
single linked lists, and one mistake I was making,
confusing the reference pointer with the data. My guess is
that if I am doing this, others are also.
22
For Beginners
By Bob Gowans
Having discussed important features of programming such
as design, input and output statements in Pascal, the use of
relevant comments and programming style we can give
ourselves a self congratulatory pat on the back, but...
before you can achieve the ultimate goal, writing that
elusive master program, complete with pop up menus and user
friendly screens you must learn to master the art of being
selectively repetitive in your programming technique. The
computer is really worth it's weight in gold when it can
carry out, on our behalf, long and boring tasks producing
results at amazing speed. For example a payroll program,
used by a large company, has to calculate net pay of each of
it's employees - this is essentially a repetitive task. The
same program may base it's calculations on variable tax
rates, depending on gross pay. Thus, the program has to
select a course of action for certain circumstances. Lets
take the issue of selection and repetition and treating each
as a separate concept briefly consider their use in program
design.
Selection
=========
When we are faced with a problem we usually embark on a
course of action arrived at through a mental process called
decision making. Decisions are an important part of our
everyday lives, we make countless decisions each day and
branch out along different courses of action to achieve what
we want. From many different alternatives we SELECT a
particular course of action. IF I get home on time tonight
THEN I will watch the game on T.V. ELSE I will read PNL
might be a simplified way to express a form of decision
making. In terms of program design decision making is an
important concept and one way to deal with this is very
similar to the human decision example given above. We would
express this as follows :-
if ( test of truth of a statement)
then
carry out a set of instructions when the test is true
else
carry out a set of instructions when the test is false
ifend
For example, what follows is a design language
implementation of a simple selection statement which tests
whether a person is married or not and writes out a message
accordingly.
23
1.1 if married = true
1.2 then
1.3 write out 'congratulations'
1.4 else
1.5 write out 'keep trying'
1.6 ifend
If the person is married he gets the message
'congratulations', if not then is presented with the 'keep
trying' message. Remember the position of the words if,
then, else and ifend and note the indentation of the actions
to be performed.
Repetition
==========
Repetitive execution of program statements involves the
use of loops. A conditional loop is controlled ie. started
or finished, by simple or complex conditions.There are
several ways in which loops can be implemented , initially
we will deal with a type that tests for the
stopping/starting condition at the beginning of a loop. At
this stage we will not be dealing with unconditional loops,
this type of loop will execute a predetermined number of
statements, however it is as well that you are aware that
they exist. If the loop is to be executed while a condition
is true, then the loop control is written as LOOP WHILE
condition. The loop will take the following form :-
loop test (ie. loop while condition is true.)
statement 1
statement 2
etc...
loopend
It is very important to observe two rules when using
conditional loops. The first rule is to initialize any
variables, prior to entering the loop, which will be updated
within the loop. The second rule is to initialize any
variables used in the loop test ( ie. the condition ) prior
to entering the loop. To illustrate the importance of this
lets consider the following design which repetitively reads
in a real number from the keyboard and sums the total of all
the numbers entered and outputs the final total.
1 initialize variables updated in the loop
2 read in first number
3 loop while number is positive
4 update total
5 read in next number
6 loopend
24
7 write out total
Note that total is a variable that is consistently updated,
within the loop and that number is the variable used to
determine the condition of the loop ( ie. continue or stop
). This information can be tabulated as follows :-
Identifier Description Type
========= =========== =====
number number input real variable
total accumulated total of
numbers input real variable
Following the top-down methodology lets individually refine
the steps given above.
Steps 2,4,5 and 7 are easy :-
2.1 read in number
4.1 set total to total + number
5.1 read in number
7.1 write out 'The total sum of all the numbers is ',total.
Lets not forget to include relevant input prompts. Steps 2.1
and 5.1 will require further refinement.
2.1 write out 'Enter any positive number (negative number to
quit)'
2.2 read in number
5.1 write out 'Enter any positive number (negative number to
end)'
5.2 read in number
The two steps that read in number at 2.2 and 5.2 are
required because the initial condition of the loop at step 3
depends on the value of number read in at step 2.2 - we
have thus met the requirement of loop rule #2 by setting
number to it's initial value. Once the loop has been entered
control passes through steps 4.1,5.1,5.2 until at step 6,
control passes back to step 3. The condition of the loop is
tested again and it's execution depends on the data entered
at step 5.2. The two input statements are necessary - the
first to test the loop initially and the second to provide
subsequent test of the loop condition.
25
Refining step three requires us to see that the
condition is true as long as number is positive. So if
number is entered as a negative the loop will terminate.
This is a simple condition and can be expressed as follows
:-
3.1 loop while number >= 0
If you are unsure what the condition operators mean,
refer to the table under the section IMPLEMENTATION OF IF
THEN ELSE which is further on in the article.
The stopping data item ( ie a negative number ) is
known as a SENTINEL VALUE. If the number variable has a
negative value the loop condition is not true and the loop
is not executed. In this case the execution of the loop is
controlled by user input.
To further refine step 1 we require to know the
variables that will be updated in the loop. Both total and
number are updated within the loop but only total is updated
by assignment ( number is updated as being input ), so total
requires to be initialized before being used in the loop.
This would necessitate setting total to zero. The complete
design is given below :
1.1 set total to 0
2.1 write out 'Enter any positive number (negative number to
quit)'
2.2 read in number
3.1 loop while number >= 0
4.1 set total to total + number
5.1 write out 'Enter any positive number (negative
quits)'
5.2 read in number
6 loopend
7.1 write out 'The total sum of all the numbers is ',total.
We have dealt, briefly with a method of selection and a
method of repetition and program language methods which
allow these concepts are called CONTROL STRUCTURES. As you
guessed there are other control structures but we will leave
those for another article. Lets go on to implement the two
control structures we have discussed in Pascal.
IMPLEMENTATION OF IF THEN ELSE
==============================
The if statements of the design are written, in Pascal,
similarly except you do not need to include the ifend
statement.
26
if condition
then
begin
statement 1;
statement 2;
........ etc
end
else
begin
statement 1;
statement 2;
......... etc
end;
Note that the Pascal reserved words BEGIN and END mark
the start and finish of multiple statements. If there is
only one statement to be executed then the BEGIN and END may
be excluded. Lets discuss the implementation of our design
concerning the married or single survey. I have added to the
basic design by stipulating an interactive program that
prompts the user to respond 'y' or 'n' to a question. This
introduces us to the data structure CHAR. The data type
char, provided by Pascal, enables us to manipulate character
sets including upper-case letters, the digits and some
punctuation marks and mathematical symbols. When the user
inputs a character - in this case either 'y' or 'n', the
character input is assigned to the char variable RESPONSE.
By using the if... then... else... control structure the
program determines what the user input; the users
response.If it is 'y' then the a message is output, if it is
'n' then a different message is output. Please note that
when testing for characters they should be included in
single quotation marks. When inputting characters they
should be written as they are - without quotation marks.
program ismarried;
var
response : char;
begin
write('Are you married ( enter y or n ) > ');
read(response);
if response = 'y' {Condition test. Note
test for character uses ''}
then
writeln('You lucky person')
else
writeln('What''s your phone number?'); {Ifend}
writeln;
27
end.
A condition is a test which is carried out on the
values of variables to compare them in some way. As your
knowledge of Pascal evolves their use will become more
apparent to you. For now lets summarize the various tests
for conditions in a table.
Operator Condition
======== =========
> is greater than
< is less than
= is equal to
<> is not equal to
>= is greater than or equal to
<= is less than or equal to
You can probably see, that comparison operators are a
powerful programming tool that can be used to test for a
variety of conditions. Now try the following exercises.
Solutions will be given at the end of the article.
Ex1
____________________________________________________________
Devise a design to read in two numbers, compare them
and print out the two numbers in ascending order.
Ex2
Length, width and height are integer variables and
woman, man and child are char variables. Which of the
following are valid conditions?
Solutions are given at end of article.
(a) length > 24
(b) woman = 'Wendy'
(c) child = 5
(d) height = 63
(e) man >= 70
(f) width = '6'
(g) child = 'C'
28
IMPLEMENTATION OF THE WHILE DO LOOP
===================================
While loops are implemented in Pascal as given below
while condition do
begin
statement1;
statement2;
.........etc
end;
The statements to be executed repeatedly are separated
by semicolons and if there is only one statement within the
loop the keywords BEGIN and END may be exclude. Now lets go
back to our design from the REPETITION section in the
article and implement this in Pascal.
program addup;
{ Adds up and prints total, to the screen, of real numbers
input by the user }
var
total,number : real;
begin
total := 0; {Initialize variables updated in loop}
write('Enter a positive number (negative number to exit)
> ');
readln(number);
while number >= 0 do { start of loop }
begin
total := total + number;
writeln;
write('Enter a positive # (neg. # to exit) > ');
readln(number)
end;{ of while do loop }
writeln;
writeln('The total sum of numbers entered is ',total:5:2)
end.
As you can see the actual implementation of the design
into Pascal was fairly straightforward, the brain work was
required at the design stage. So while you are in the mood
try out the following exercise :-
Ex3
Design a program that will read in the ages of all the
participants, the program will keep a count of the total
amount of users and will print out the maximum age plus the
total amount of users. Hint - do not forget the sentinel.
29
Before I give the solutions to the exercises I thought
I would include this useful Turbo Pascal routine that checks
the amount of space available on a default hard disk. The
routine is not really aimed at beginners but gives you an
insight into the power of Turbo Pascal and it's control over
DOS. To more experienced users, feel free to modify the code
to suit your own needs.
program HDcheck;
uses crt,dos;
var
i : integer;
totalspace,usedspace,freespace : real;
begin
clrscr;
writeln;
totalspace := disksize(0)/1000000;
writeln('Total disk space = ',totalspace:5:2,' MBytes');
writeln;
usedspace := (disksize(0) - diskfree(0))/1000000;
writeln('Disk space used = ',usedspace:5:2,' MBytes');
writeln;
freespace := diskfree(0)/1000000;
writeln('Space free = ',freespace:5:2,' MBytes');
for i := 1 to 15 do
writeln;
writeln('HDCheck by B.Gowans');
writeln;
writeln('Press enter to continue');
readln;
clrscr
end.
The use of control statements such as IF...THEN...ELSE
and WHILE DO will greatly enhance your programming abilities
and this is just the start of the control structures
available to Pascal. Practice using these structures by
following the examples found in the text books and get
comfortable in using them. Here are the solutions to the
exercises
Ex1 :
1 initialize variables
2 compare the numbers
3 write out numbers in order
1.1 read in number1
30
1.2 read in number2
2.1 if number1 > number2
2.2 then
2.3 write out number1
2.4 write out number2
2.5 else
2.6 write out number2
2.7 write out number1
2.8 ifend
Ex2 :
(a) valid
(b) invalid - variable is of type char and not string.
(c) invalid - variable is of type char and not integer.
(d) valid
(e) invalid - for same reasons as (c).
(f) invalid - width is integer variable and not char.
(g) valid.
Ex3 :
1. initialize variables
2. loop while users to be processed
3. process age
4. process count
5. loopend
6. write out count, maxage
Further refinement :-
1.1 set count to 0
1.2 set max to 0
1.3.1 write out 'Please enter age ( enter -1 to quit )'
1.3.2 read in age
2.1 loop while age < > -1
3.1 if age > max
3.2 then
3.3 set max to age
3.4 else
3.5 { do nothing }
3.6 ifend
4.1 set count to count + 1
4.2 write out 'Please enter age ( enter -1 to quit )'
4.3 read in age
5 loopend
6.1 write out 'The maximum age was ',maxage
6.2 write out 'Out of a total of ',count,' people.'
The implementation in Pascal follows - please note there may
31
be small changes in the implementation which does not affect
the overall design.
program age_survey;
var
age : integer;
maxage : integer;
count : integer;
begin
write('Enter age to nearest year ( -1 to quit ) > ');
readln(age);
count := 0;
maxage := 0;
while age <> -1 do { start loop, stop with age = -1 }
begin
if age > maxage { if start }
then
maxage := age
else
{ do nothing };{no other branching is necessary so ifend}
count := count + 1;
writeln;
write('Enter age to nearest year (-1 to quit ) > ');
readln(age)
end;
writeln;
writeln('The greatest age was ',maxage,' years');
writeln;
writeln('The total number surveyed was ',count)
end.
All the code in this article has been written and
compiled using Turbo Pascal v4. The executable code was run
on an Opus PC 5 under DOS 3.3. There is no guarantee that
the same code will run under your own particular
configuration.
32
Recursion is Easy
By Chris Burke
Recursion and pointers seem to be the two subjects that
cause beginners in pascal the most heartache. This brief
article aims to delve into the subject of recursion.
Introduction
Imagine you (Alfred) are talking to Bob, when Christine
walks up and rudely interrupts and starts talking to you.
You put up with it and talk to Christine (she is prettier
than Bob anyway). While talking to Christine, her mum
(Daphne) wants a bit of a chat - you being the wise person
you are (and liking Christine so much) decide to talk to
Daphne, just after finishing your chat and commencing
conversing with Christine, the telephone rings - you answer
and (OH NO!!) it is Elaine wanting to go to the movies
tonight, you quickly say sure no problems see you at 8, see
you then, bye and continue talking to Christine. After a
little while Christine leaves and you restart talking to
Bob.
So, What have you been doing - well
1. You have been two-timing, and deserve all you get.
2. You have been using your 'Talk' function recursively.
i.e.
Talk(Bob)
Talk(Christine)
Talk(Daphne)
Return to Talk(Christine)
Talk(Elaine)
Return to Talk(Christine)
Return to Talk(Bob)
So now that you understand a bit about the concepts of
recursion lets go onto more computer oriented problems of
recursion.
One of the most common examples of recursion is the
factorial program - as it gets quoted so often I shall use
the example here. For those not familiar with what a
factorial is - here is an explanation :
The factorial of a positive number (0,1,2...) is
defined as follows :
factorial of N = N*(N-1)*(N-2)....*1
factorial of 0 = 1 (by definition - i.e. for no real
reason)
33
(For those with a little curiosity, there is a related
function called the gamma function which works on negative
numbers and non-integers.)
The factorial is represented using the ! symbol
i.e. 12! represents the factorial of 12
The first way the programmer would go about defining a
function to calculate the factorial is as follows :
function factorial(N:longint):longint;
var
Count,Temp:longint;
begin
Temp:=1;
for Count:=1 to N do
Temp:=Temp*Count;
Factorial:=Temp;
end;
which would in effect for N=5 calculate the following
factorial(N) = 1*2*3*4*5
which adheres to the formula above.
The Quantum Leap
The quantum leap in thought is to realize that the
above formula:
N!=N*(N-1)*(N-2)*(N-3)...*1 can be written differently.
Lets take an example, say 5!.
5!=5*(5-1)*(5-2)*(5-3)*(5-4)
or =5* 4 * 3 * 2 * 1 BUT LOOK!! this can be
written
=5* 4!
and
4!=4*3*2*1 = 4*3! AND SO ON.....
which means the original formula can be written as
N!=N*(N-1)! - This is the mathematical equivalent of
recursion.
converting this to pascal is a fairly simple step :
34
function factorial(N:longint):longint;
begin
factorial:=N*factorial(N-1)
end;
EASY - just a straight implementation in pascal....
The only thing left to do is to implement what is known
as an exit condition - or a termination condition, this
tells the above factorial function when to stop what is
currently an infinite loop, the easiest way to do this is to
realize that factorial(0)=1 as defined by our mathematical
gurus. The modified code is :
function factorial(N:longint):longint;
begin
if N=0 then factorial:=1
else factorial:=N*factorial(N-1)
end;
This is where the IDE environment in the turbo pascal
compiler comes in handy. Write a little program that
includes the above function as follows :
program TestRecursion;
function factorial(N:longint):longint;
begin
if N=0 then factorial:=1
else factorial:=N*factorial(N-1)
end;
begin
writeln('Factorial 5=',factorial(5));
writeln('Factorial 0=',factorial(0));
end.
Try tracing through this code remembering to use the
'step over' on the 'begin' and the 'step into' on the two
writeln statements. After a little playing and placing
watches on N. (Don't forget to put Local Symbols and Debug
Information on in the options). By now recursion should be
somewhat more obvious.
Something A Little More Useful
This subject may be of a little more interest, but as I
have always found - the best way of learning is by doing,
and when you have it working you will be far more satisfied.
The following hints should get you on your way to a
routine which does some function MYFUNC on every file on
your hard disk - in every directory on your hard drive.
35
In english here is what you would do :
1. Write a routine which does MYFUNC on every file in a
specified
directory something like the following :
procedure DoMyFuncHere(D:Pathstring);
var
F:FileString;
begin
F:=GetFirstName; (* Remember : *)
while F is valid do (* we want all directories
and files *)
begin
MYFUNC(D+F);
F:=GetNextName;
end;
end;
test this procedure out and see if it works - OK
2. Modify this routine so that it calls itself recursively
when there is a subdirectory (remember - don't get worried
by the fact that the routine is calling itself)
{ Ed. Note: Semi-Pascal algorithm }
| procedure DoMyFuncHere(D:Pathstring);
| var
| F:FileString;
|
| begin
| F:=GetFirstName; (* Remember : *)
| while F is valid do (* we want all directories and
files *)
| begin
| if (F='.') or (F='..') then
|
DontGoHereThereIsProblemsIfYouDo;
| else if F_IsADirectory then
| DoMyFuncHere(D+F)
| else MYFUNC(D+F);
| F:=GetNextName;
| end;
| end;
{ Ed. Note: Actual Pascal algorithm }
~program test;
~uses crt,dos;
~
~ Procedure MyFunc (FileRec : SearchRec);
~ begin
~ Writeln(FileRec.Name);
~ end;
~
36
~ procedure DoMyFuncHere(D:Pathstr);
~ var
~ SR : SearchRec;
~
~ begin
~ FindFirst(D+'*.*',Directory+Archive,SR);
~ while DosError = 0 do
~ begin
~ if (SR.Name <> '.') AND (SR.Name <> '..') then
~ If (SR.Attr AND Directory) > 0 then
~ DoMyFuncHere(SR)
~ else
~ MYFUNC(D+SR.NAME);
~ FindNext(SR);
~ end;
~ end;
~
~begin
~ DoMyFuncHere('C:\');
~end.
Try this out by calling DoMyFuncHere('C:\');
I am sure you can put your imagination to other uses for
such a recursive directory engine, ie: finding and deleting
ALL BAK files on a drive, or counting the size of
subdirectories, you can modify the search criterion in the
FINDFIRST line.
A BIT OF EXPLAINING
------------------
Imagine a simple directory like this,
COMMAND.COM 1 calls MYFUNC
AUTOEXEC.BAT 1 calls MYFUNC
<DOS> 1 calls DoMyFuncHere - recursively
<.> 2 skips
<..> 2 skips
FDISK.COM 2 calls MYFUNC
DEBUG.COM 2 calls MYFUNC
2 returns from second level
CONFIG.SYS 1 calls MYFUNC
<UTILS> 1 calls DoMyFuncHere - recursively
<.> 2 skips
<..> 2 skips
LIST.COM 2 calls MYFUNC
<NU> 2 calls DoMyFuncHere - recursively
<.> 3 skips
<..> 3 skips
SI.EXE 3 calls MYFUNC
3 returns from third level
37
4EDIT.EXE 2 calls MYFUNC
2 returns from second level
4DOS.COM 1 calls MYFUNC
1 returns and exits to main program
By following the above (the number represents how deep
you have gone) you should be able to see how simple the
program to look at every file on your hard disk is.
If you want any more assistance on recursion see the section
above called 'A BIT OF EXPLAINING' otherwise continue to the
conclusion.
Conclusion
I hope that this description of recursion will answer
your questions on recursion, if you have any questions for
me then I have a point called 'Mindware' fidonet 3:640/305.1
Christopher Burke
Brisbane, Queensland
AUSTRALIA
Chris Burke is owner/operator of a contract Programming
company called Mindware in Brisbane, Australia. He programs
commercially using various languages including Turbo Pascal
from version 2.0 through to version 5.5 professional (Using
Object Professional), Assembler, and Clarion.
He runs a point Fidonet system, and can be contacted at
3:640/305.1.
38
Conclusion
There are a couple things I'd like to point about some
future issues. Bob Gowans will, of course, continue to produce
his column, which has become incredibly popular. Richard is going
to give us quite bit of stuff on graphics in Pascal, so that will
also be a regular contribution. I will be starting a series
pretty soon on doing large projects. I am just finishing up a
large project that involved 5 other programmers and quite a bit
of design work before the implementation. I'd like to go over a
lot of the stuff we did. This alone should take several issues,
but I think you will find it very interesting. The code alone,
because it is so vast, will be broken up into several issues and
each unit will be covered alone and then the final integration of
the entire project will be examined.
I feel bad that I can't seem to get the newsletter out as
often as I would like, but like many of you, I have to set my
priorities, one of them, earning a living. Hopefully you will
forgive me for this. All I can do is make a promise that I will
try my best to get each issue out as soon as possible.
I would also like to ask, as I do in every issue, that you
try to find the time to send in some of your own material for
inclusion in the newsletter. Without user contributions, I just
can't keep this going. They have been good lately, but I could
handle a few more. So, please, if you don't feel you can
contribute, try to find friends that can. Try to get them to send
in stuff they've done.
One last word on the distribution list. I keep quite a bit
of the updates in a directory which I search at the end of the
month to make updates to the distribution list. Several days ago,
because of a strange problem, that directory, almost exclusively,
was completely wiped out. I have been unable to make any
corrections to the distribution list. If you sent in a
correction, or addition, please send me the information again so
that I can make those corrections. I'm sorry for any
inconvenience this may have caused.
Ah, and one final thing that I almost forgot. I have
recently put a second computer together. This should help me put
a little more time into the newsletter, as I won't have to worry
about bringing my BBS down every time I want to work on the
newsletter.
Well, enjoy the holidays and hopefully I'll have a brand new
issue out in early January.
_Pete Davis
39
The Pascal NewsLetter is Copyrighted by Pete Davis.
Articles submitted by others are the property of the authors and
are used with permission. They may not be treated separately
from this newsletter without the author's permission and thus
maintain all distribution rules of the newsletter as a whole. It
may be freely distributed in un-modified form, but no charge
whatsoever may be incurred on the recipient. All code is provided
'as-is' with no guarantees whatsoever.
The Pascal NewsLetter can be obtained from the
following locations:
GEnie : General Electric Network for Information
Exchange. It is located in the IBMPC
filelist.
Simtel : Internet address: 26.2.0.74 or
Simtel20.ARPA It is located in the
<MSDOS.PASCAL> directory.
Programmer's Forum : This is the home of the PNL.
Each issue can be found in
the MAG directory from the main area.
The number is on the title page.
If you would like to receive back issues of PNL
directly from me, send a diskette and $2.00 for shipping.
Don't forget to include your address.
Send your order to:
Peter Davis
4851 Brandywine St. NW
Washington, DC 20016
If you are a SysOp that will regularly carry PNL and
would like to have your bulletin board listed as such, here,
send me a message either by postal mail or at one of the
electronic addresses given on the title page, with your
bulletin board's name, phone number, and your name.
40
Distribution List
The following is the phone numbers to bulletin boards known
to carry PNL. If you would like your bulletin board's name and
number added to or deleted from this list, please send me a
message at one of my many addresses. I can not guarantee whether
a listed board will have any particular issue.
The Programmer's Forum ................. Phone: (202) 966-3647
The Programmer's Forum is the home of the PNL.
BBS | FidoNet Addrs | Phone #
--------------------------+----------------------+---------------
The Bored | 1:397/3 | (512) 303-0471
Classic City BBS | 1:370/10 | (404) 548-0726
Thieve's World BBS | 1:106/805 | (713) 463-8053
Hippocampus BBS | 1:141/205 | (203) 484-4621
Rogers State College | 1:170/708 | (918) 341-8982
The Foxtrot BBS | 1:272/26 | (914) 567-1814
Turbo City BBS | 1:208/2 | (209) 599-7435
Austin IEMUG/MIDI BBS | 1:382/14 | (512) 258-0626
Laser Publishers | 1:170/403 | (918) 438-2749
Fargo RBBS-PC | 1:10/8 | (701) 293-5973
Momentary Lapse of Reason | | (704) 327-6361
The Demon's Den | | (508) 433-2702
The Cannibal Cafe | | (508) 840-6589
IBM Tech FIDO | | (508) 433-6491
The User Friendly BBS | | (704) 323-8223
The Disseminator BBS | 3:640/378 | 61-7-368-1239
Beyound Fronttiers BBS | 2:230/101 | (+45)8694-1609
Collision Theory | | (703) 503-9441
Merlin's Mailbox | 2:245/39 |
Ed-Net9600 | 1:153/734 | (604) 732-8877